import { defineStore } from "pinia"
import { getToken, removeToken, setToken } from "../utils/token-utils"
import type { UserInfoState } from "./interface"
import { allAsyncRoutes, staticRoutes, anyRoutes } from "@/router/routes"
import { reqLogin, reqUserInfo, reqLogout } from "@/api/user"
import type { RouteRecordRaw } from "vue-router"
import router from "@/router"
import { cloneDeep } from "lodash"

//1.封装一个函数,用来根据用户的权限过滤异步路由表
//接收两个参数,参数1是异步路由表 参数2是用户权限的路由数组
function filterAsyncRoutes(
  allAsyncRoutes: Array<RouteRecordRaw>,
  routesName: string[]
) {
  //逻辑：根据用户的路由数组的值,过滤异步路由,得到过滤出来的符合条件的异步路由中的路由组成的数组，这样就可以使不同权限的用户看到的菜单是不一样的
  return allAsyncRoutes.filter((item) => {
    //判断当前1级路由item的name有没有在用户权限路由数组中出现
    if (routesName.includes(item.name as string)) {
      //递归一定要写条件,否则会出现栈溢出
      //没有子路由就停止
      if (item.children?.length) {
        //如果当前的1级路由确实出现在了当前用户的权限路由数组中,则还要看当前符合条件的1级路由中2级路由有哪些也是符合条件的，需要用到递归
        //得到2级路由过滤出来的结果,将其替换为一级路由要携带走的children子路由，最终交给静态路由表进行展示
        item.children = filterAsyncRoutes(
          item.children as RouteRecordRaw[],
          routesName
        )
      }
      //filter只有在内部函数返回true的时候,才会进行过滤出来
      return true
    }
  })
}

//2.因为添加新的路由映射关系只有addRoute方法,每次只能添加一个,所以我们可以封装一个addRoutes方法,每次能添加多个
//该方法接收一个参数,就是要添加到路由映射上的路由表数组
function addRoutes(routes: Array<RouteRecordRaw>) {
  routes.forEach((item) => {
    router.addRoute(item)
  })
}

//3.封装一个函数，当退出登录的时候，对动态路由清理。
function resetRoutes() {
  //排他法，先清理掉所有的映射关系，然后再添加常量路由的映射关系
  //路由对象有一个removeRoute方法,用来删除某个映射关系,参数是当前路由的name属性值
  //路由对象有一个getRoutes方法,用来得到所有的映射关系
  const routes = router.getRoutes()
  routes.forEach((item) => {
    router.removeRoute(item.name as string)
  })
  //然后把静态路由重新添加给路由对象
  addRoutes(staticRoutes)
  //不能在退出登录排他的时候 重新添加任意路由,因为任意路由只能最后添加,如果现在添加了,则未来再去添加异步路由的时候,异步路由就在404后边了,匹配就会出现问题
  // addRoutes(anyRoutes)
}

/**
 * 用户信息
 * @methods setUserInfos 设置用户信息
 */
export const useUserInfoStore = defineStore("userInfo", {
  state: (): UserInfoState => ({
    token: getToken() as string,
    userInfo: {
      routes: [],
      buttons: [],
      roles: [],
      name: "",
      avatar: "",
    },
    menuRoutes: staticRoutes,
  }),

  /*
  分析login登录需要做的事情
    1.发起请求获取token
    2.将获取到的token保存在state中
    3.token需要数据持久化，所以需要在localStroage中也保存一份token
  */
  actions: {
    //登录的请求方法
    async login(username: string, password: string) {
      const result = await reqLogin(username, password)
      this.token = result.token
      setToken(result.token)
    },

    //获取用户信息的方法
    //1.发送请求得到用户信息
    //2.把用户信息交给state保存
    async getInfo() {
      const userInfo = await reqUserInfo()
      this.userInfo = userInfo

      //此时拿到用户的路由权限数组 routes
      //也可以拿到异步路由表
      //我们就进行对比,看一下异步路由表中哪些路由符合权限数组,如果符合,则把这个异步路由添加到静态路由中，最终进行展示
      //使用cloneDeep是因为当用户退出登录后登陆其它用户时，在不刷新页面的情况下其它用户展示的路由菜单和上一个用户的是一样的，这是filterAsyncRoutes函数里面给子路由重新赋值产生的问题，所以把异步路由表深拷贝一份，每次换了用户登陆时不会改变原始的异步路由表
      const filterRoutes = filterAsyncRoutes(
        cloneDeep(allAsyncRoutes),
        userInfo.routes
      )
      // console.log(filterRoutes)
      //拿到过滤出来的路由信息以后,把路由添加到静态路由中
      //方法1：
      // filterRoutes.forEach((item) => {
      //   staticRoutes.push(item)
      // })
      //方法2：
      this.menuRoutes = staticRoutes.concat(filterRoutes, anyRoutes)

      //新增路由映射关系
      addRoutes(filterRoutes)
      addRoutes(anyRoutes)
    },

    //退出登录的方法
    //1.发起退出登录的请求
    //2.清空本地和localStorage的token和用户信息
    async logout() {
      await reqLogout()

      //数据和方法都在当前的store对象上,this指向store对象
      this.reset()
    },

    reset() {
      // 删除local中保存的token
      removeToken()
      // 提交重置用户信息的mutation
      this.token = ""
      this.userInfo = {
        routes: [],
        buttons: [],
        roles: [],
        name: "",
        avatar: "",
      }
      //退出登录的时候,需要重置一下路由映射关系
      resetRoutes()
    },
  },
})
